/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/object_factory.h"
#include "plugins/ecmascript/runtime/js_handle.h"
#include "gtest/gtest.h"
#include "test_helper.h"
#include "plugins/ecmascript/runtime/js_hclass.h"
#include "plugins/ecmascript/runtime/js_tagged_value-inl.h"
#include "plugins/ecmascript/runtime/js_object.h"
#include "plugins/ecmascript/runtime/js_function.h"
#include "plugins/ecmascript/runtime/js_primitive_ref.h"
#include "plugins/ecmascript/runtime/js_symbol.h"
#include "plugins/ecmascript/runtime/js_thread.h"
#include "include/runtime.h"
#include "plugins/ecmascript/runtime/ecma_vm.h"
#include "plugins/ecmascript/runtime/global_env.h"

// NOLINTNEXTLINE(google-build-using-namespace)
using namespace ark::ecmascript;
using ark::coretypes::ReinterpretDoubleToTaggedType;
using ark::coretypes::ReinterpretTaggedTypeToDouble;

// NOLINTBEGIN(readability-magic-numbers)

namespace ark::test {
class JSTaggedValueTest : public testing::Test {
public:
    void SetUp() override
    {
        TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_);
    }

    void TearDown() override
    {
        TestHelper::DestroyEcmaVMWithScope(instance_, scope_);
    }

protected:
    // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
    JSThread *thread_ {};

private:
    PandaVM *instance_ {nullptr};
    ecmascript::EcmaHandleScope *scope_ {nullptr};
};

TEST_F(JSTaggedValueTest, Double)
{
    double d = 1.1;
    JSTaggedValue td(d);
    EXPECT_EQ(true, td.IsDouble());
    EXPECT_EQ(false, td.IsInt());
    EXPECT_EQ(false, td.IsObject());
    ASSERT_DOUBLE_EQ(td.GetDouble(), d);

    double nan = std::nan("");
    JSTaggedValue tNan(nan);
    EXPECT_EQ(true, tNan.IsDouble());
    EXPECT_EQ(false, tNan.IsInt());
    EXPECT_EQ(false, tNan.IsObject());
    EXPECT_EQ(ReinterpretDoubleToTaggedType(tNan.GetDouble()), ReinterpretDoubleToTaggedType(nan));

    double pureNaN = ReinterpretTaggedTypeToDouble(JSTaggedValue::TAG_INT - JSTaggedValue::DOUBLE_ENCODE_OFFSET);
    EXPECT_EQ(true, JSTaggedValue::IsImpureNaN(pureNaN));
}

TEST_F(JSTaggedValueTest, Int)
{
    int i = 0x5c;
    JSTaggedValue t(0x5c);
    EXPECT_EQ(true, t.IsInt());
    EXPECT_EQ(false, t.IsObject());
    EXPECT_EQ(false, t.IsDouble());
    EXPECT_EQ(t.GetInt(), i);
}

TEST_F(JSTaggedValueTest, IsObject)
{
    auto *p = reinterpret_cast<ark::ObjectHeader *>(0xffff0000UL);
    JSTaggedValue t(p);
    EXPECT_EQ(true, t.IsObject());
    EXPECT_EQ(false, t.IsInt());
    EXPECT_EQ(false, t.IsDouble());
    EXPECT_EQ(t.GetHeapObject(), p);
}

TEST_F(JSTaggedValueTest, False)
{
    JSTaggedValue t = JSTaggedValue::False();
    EXPECT_EQ(t.IsFalse(), true);
}

TEST_F(JSTaggedValueTest, True)
{
    JSTaggedValue t = JSTaggedValue::True();
    EXPECT_EQ(t.IsTrue(), true);
}

TEST_F(JSTaggedValueTest, Undefined)
{
    JSTaggedValue t = JSTaggedValue::Undefined();
    EXPECT_EQ(t.IsUndefined(), true);
}
TEST_F(JSTaggedValueTest, Null)
{
    JSTaggedValue t = JSTaggedValue::Null();
    EXPECT_EQ(t.IsNull(), true);
}

TEST_F(JSTaggedValueTest, Hole)
{
    JSTaggedValue t = JSTaggedValue::Hole();
    EXPECT_EQ(t.IsHole(), true);
}

TEST_F(JSTaggedValueTest, ToPrimitive)
{
    JSTaggedValue result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result.GetInt(), 100);

    JSTaggedValue doubleV((double)100.0);
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, doubleV));
    EXPECT_EQ(result.GetDouble(), (double)100.0);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_TRUE(result.IsUndefined());

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_TRUE(result.IsHole());

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_TRUE(result.IsNull());

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_TRUE(result.IsFalse());

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToPrimitive(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_TRUE(result.IsTrue());
}

TEST_F(JSTaggedValueTest, ToBoolean)
{
    EXPECT_TRUE(JSTaggedValue(100).ToBoolean());
    EXPECT_FALSE(JSTaggedValue(0).ToBoolean());

    EXPECT_TRUE(JSTaggedValue((double)100.0).ToBoolean());
    EXPECT_FALSE(JSTaggedValue(std::nan("")).ToBoolean());

    EXPECT_FALSE(JSTaggedValue::Undefined().ToBoolean());

    EXPECT_FALSE(JSTaggedValue::Hole().ToBoolean());

    EXPECT_FALSE(JSTaggedValue::Null().ToBoolean());

    EXPECT_FALSE(JSTaggedValue::False().ToBoolean());
    EXPECT_TRUE(JSTaggedValue::True().ToBoolean());

    EXPECT_FALSE(thread_->GetEcmaVM()->GetFactory()->GetEmptyString().GetTaggedValue().ToBoolean());
    EXPECT_TRUE(thread_->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().ToBoolean());
}

TEST_F(JSTaggedValueTest, ToNumber)
{
    JSTaggedNumber result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result.GetNumber(), 100);

    JSTaggedValue doubleV((double)100.0);
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, doubleV));
    EXPECT_EQ(result.GetNumber(), (double)100.0);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToNumber(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result.GetNumber(), 1);

    JSHandle<JSTaggedValue> stringV0(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 1234 "));
    result = JSTaggedValue::ToNumber(thread_, stringV0);
    EXPECT_EQ(result.GetNumber(), 1234);

    JSHandle<JSTaggedValue> stringV1(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0b1010 "));
    result = JSTaggedValue::ToNumber(thread_, stringV1);
    EXPECT_EQ(result.GetNumber(), 10);

    JSHandle<JSTaggedValue> stringV2(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0O11 "));
    result = JSTaggedValue::ToNumber(thread_, stringV2);
    EXPECT_EQ(result.GetNumber(), 9);

    JSHandle<JSTaggedValue> stringV3(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0x2d "));
    result = JSTaggedValue::ToNumber(thread_, stringV3);
    EXPECT_EQ(result.GetNumber(), 45);

    JSHandle<JSTaggedValue> stringV4(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0.000001 "));
    result = JSTaggedValue::ToNumber(thread_, stringV4);
    EXPECT_EQ(result.GetNumber(), 0.000001);

    JSHandle<JSTaggedValue> stringV5(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 1.23 "));
    result = JSTaggedValue::ToNumber(thread_, stringV5);
    EXPECT_EQ(result.GetNumber(), 1.23);

    JSHandle<JSTaggedValue> stringV6(thread_->GetEcmaVM()->GetFactory()->NewFromString(" -1.23e2  "));
    result = JSTaggedValue::ToNumber(thread_, stringV6);
    EXPECT_EQ(result.GetNumber(), -123);

    JSHandle<JSTaggedValue> stringV7(thread_->GetEcmaVM()->GetFactory()->NewFromString(" -123e-2"));
    result = JSTaggedValue::ToNumber(thread_, stringV7);
    EXPECT_EQ(result.GetNumber(), -1.23);

    JSHandle<JSTaggedValue> stringV8(thread_->GetEcmaVM()->GetFactory()->NewFromString("  Infinity "));
    result = JSTaggedValue::ToNumber(thread_, stringV8);
    EXPECT_TRUE(std::isinf(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV9(thread_->GetEcmaVM()->GetFactory()->NewFromString("100e307"));
    result = JSTaggedValue::ToNumber(thread_, stringV9);
    EXPECT_TRUE(std::isinf(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV10(thread_->GetEcmaVM()->GetFactory()->NewFromString("  ."));
    result = JSTaggedValue::ToNumber(thread_, stringV10);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV11(thread_->GetEcmaVM()->GetFactory()->NewFromString("12e+"));
    result = JSTaggedValue::ToNumber(thread_, stringV11);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV12(thread_->GetEcmaVM()->GetFactory()->NewFromString(".e3"));
    result = JSTaggedValue::ToNumber(thread_, stringV12);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV13(thread_->GetEcmaVM()->GetFactory()->NewFromString("23eE"));
    result = JSTaggedValue::ToNumber(thread_, stringV13);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV14(thread_->GetEcmaVM()->GetFactory()->NewFromString("a"));
    result = JSTaggedValue::ToNumber(thread_, stringV14);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV15(thread_->GetEcmaVM()->GetFactory()->NewFromString("0o12e3"));
    result = JSTaggedValue::ToNumber(thread_, stringV15);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV16(thread_->GetEcmaVM()->GetFactory()->NewFromString("0x12.3"));
    result = JSTaggedValue::ToNumber(thread_, stringV16);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV17(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 12.4."));
    result = JSTaggedValue::ToNumber(thread_, stringV17);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV18(thread_->GetEcmaVM()->GetFactory()->NewFromString("123test"));
    result = JSTaggedValue::ToNumber(thread_, stringV18);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV19(thread_->GetEcmaVM()->GetFactory()->NewFromString("123test"));
    result = JSTaggedValue::ToNumber(thread_, stringV19);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV20(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0b "));
    result = JSTaggedValue::ToNumber(thread_, stringV20);
    EXPECT_TRUE(std::isnan(result.GetNumber()));

    JSHandle<JSTaggedValue> stringV21(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0b0000 "));
    result = JSTaggedValue::ToNumber(thread_, stringV21);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV22(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0o0000 "));
    result = JSTaggedValue::ToNumber(thread_, stringV22);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV23(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0X0000 "));
    result = JSTaggedValue::ToNumber(thread_, stringV23);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV24(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 000.00000 "));
    result = JSTaggedValue::ToNumber(thread_, stringV24);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV25(thread_->GetEcmaVM()->GetFactory()->NewFromString(""));
    result = JSTaggedValue::ToNumber(thread_, stringV25);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV26(thread_->GetEcmaVM()->GetFactory()->NewFromString("   "));
    result = JSTaggedValue::ToNumber(thread_, stringV26);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV27(thread_->GetEcmaVM()->GetFactory()->NewFromString("0"));
    result = JSTaggedValue::ToNumber(thread_, stringV27);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV28(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 0 "));
    result = JSTaggedValue::ToNumber(thread_, stringV28);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV29(thread_->GetEcmaVM()->GetFactory()->NewFromString("00000000"));
    result = JSTaggedValue::ToNumber(thread_, stringV29);
    EXPECT_EQ(result.GetNumber(), 0);

    JSHandle<JSTaggedValue> stringV30(thread_->GetEcmaVM()->GetFactory()->NewFromString(" 00000000 "));
    result = JSTaggedValue::ToNumber(thread_, stringV30);
    EXPECT_EQ(result.GetNumber(), 0);

    thread_->ClearException();
    JSHandle<JSTaggedValue> symbolV1(thread_->GetEcmaVM()->GetFactory()->NewJSSymbol());
    JSTaggedValue::ToNumber(thread_, symbolV1);
    EXPECT_TRUE(thread_->HasPendingException());
    EXPECT_TRUE(thread_->GetException().IsJSError());
}

TEST_F(JSTaggedValueTest, ToInteger)
{
    JSTaggedNumber result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result.GetNumber(), 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result.GetNumber(), (double)100.0);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result.GetNumber(), (double)100);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToInteger(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result.GetNumber(), 1);
}

TEST_F(JSTaggedValueTest, ToInt32)
{
    int32_t result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    double input1 = (static_cast<uint64_t>(UINT32_MAX) + 1) + 12345;
    JSTaggedValue doubleV3(input1);
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 12345);

    double input2 = 100 * (static_cast<uint64_t>(UINT32_MAX) + 1) + 23456;
    JSTaggedValue doubleV4(input2);
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 23456);

    double input3 = 100 * (static_cast<uint64_t>(UINT32_MAX) + 1) + INT32_MAX + 1 + 23456;
    JSTaggedValue doubleV5(input3);
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, 23456 - static_cast<int32_t>(INT32_MAX) - 1);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToInt32(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToUint32)
{
    uint32_t result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    double input1 = (static_cast<uint64_t>(UINT32_MAX) + 1) + 12345;
    JSTaggedValue doubleV3(input1);
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 12345);

    double input2 = 100 * (static_cast<uint64_t>(UINT32_MAX) + 1) + 23456;
    JSTaggedValue doubleV4(input2);
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 23456);

    double input3 = 100 * (static_cast<uint64_t>(UINT32_MAX) + 1) + INT32_MAX + 1 + 23456;
    JSTaggedValue doubleV5(input3);
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, static_cast<uint64_t>(INT32_MAX) + 1 + 23456);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToUint32(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToInt16)
{
    int32_t result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    double input1 = (static_cast<uint64_t>(UINT16_MAX) + 1) + 12345;
    JSTaggedValue doubleV3(input1);
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 12345);

    double input2 = 100 * (static_cast<uint64_t>(UINT16_MAX) + 1) + 23456;
    JSTaggedValue doubleV4(input2);
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 23456);

    double input3 = 100 * (static_cast<uint64_t>(UINT16_MAX) + 1) + INT16_MAX + 1 + 23456;
    JSTaggedValue doubleV5(input3);
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, 23456 - static_cast<int32_t>(INT16_MAX) - 1);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToInt16(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToUint16)
{
    uint32_t result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    double input1 = (static_cast<uint64_t>(UINT16_MAX) + 1) + 12345;
    JSTaggedValue doubleV3(input1);
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 12345);

    double input2 = 100 * (static_cast<uint64_t>(UINT16_MAX) + 1) + 23456;
    JSTaggedValue doubleV4(input2);
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 23456);

    double input3 = 100 * (static_cast<uint64_t>(UINT16_MAX) + 1) + INT16_MAX + 1 + 23456;
    JSTaggedValue doubleV5(input3);
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, static_cast<uint64_t>(INT16_MAX) + 1 + 23456);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToUint16(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToInt8)
{
    int32_t result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    double input1 = (static_cast<uint64_t>(UINT8_MAX) + 1) + 45;
    JSTaggedValue doubleV3(input1);
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 45);

    double input2 = 100 * (static_cast<uint64_t>(UINT8_MAX) + 1) + 56;
    JSTaggedValue doubleV4(input2);
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 56);

    double input3 = 100 * (static_cast<uint64_t>(UINT8_MAX) + 1) + INT8_MAX + 1 + 23;
    JSTaggedValue doubleV5(input3);
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, 23 - static_cast<int32_t>(INT8_MAX) - 1);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToInt8(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToUint8)
{
    uint32_t result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    double input1 = (static_cast<uint64_t>(UINT8_MAX) + 1) + 34;
    JSTaggedValue doubleV3(input1);
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 34);

    double input2 = 100 * (static_cast<uint64_t>(UINT8_MAX) + 1) + 45;
    JSTaggedValue doubleV4(input2);
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 45);

    double input3 = 100 * (static_cast<uint64_t>(UINT8_MAX) + 1) + INT8_MAX + 1 + 56;
    JSTaggedValue doubleV5(input3);
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, static_cast<uint64_t>(INT8_MAX) + 1 + 56);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToUint8(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToUint8Clamp)
{
    uint32_t result;

    JSTaggedValue intV1(-100);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, intV1));
    EXPECT_EQ(result, 0);

    JSTaggedValue intV2(100);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, intV2));
    EXPECT_EQ(result, 100);

    JSTaggedValue intV3(300);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, intV3));
    EXPECT_EQ(result, 255);

    JSTaggedValue doubleV1((double)-100.123);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result, 0);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV3((double)100.55);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result, 101);

    JSTaggedValue doubleV4((double)99.9);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result, 100);

    JSTaggedValue doubleV5((double)300.5);
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, doubleV5));
    EXPECT_EQ(result, 255);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result, 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result, 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result, 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result, 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToUint8Clamp(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result, 1);
}

TEST_F(JSTaggedValueTest, ToPropertyKey)
{
    JSTaggedValue result;
    JSHandle<EcmaString> str = thread_->GetEcmaVM()->GetFactory()->NewFromString("null");
    JSTaggedValue key = str.GetTaggedValue();
    result = JSTaggedValue::ToPropertyKey(thread_, JSHandle<JSTaggedValue>(thread_, key)).GetTaggedValue();
    EXPECT_TRUE(key == result);
}

void CheckOkString(JSThread *thread, const JSHandle<JSTaggedValue> &tagged, PandaString &rightCStr)
{
    JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, tagged);
    JSHandle<EcmaString> rightString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rightCStr);
    EXPECT_TRUE(EcmaString::StringsAreEqual(EcmaString::Cast(result.GetObject<EcmaString>()),
                                            EcmaString::Cast(rightString.GetObject<EcmaString>())));
}

TEST_F(JSTaggedValueTest, ToString)
{
    PandaString rightCStr;
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue()), rightCStr);

    rightCStr = "undefined";
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined()), rightCStr);

    rightCStr = "null";
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()), rightCStr);

    rightCStr = "true";
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()), rightCStr);

    rightCStr = "false";
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()), rightCStr);

    rightCStr = "hello world";
    CheckOkString(thread_,
                  JSHandle<JSTaggedValue>(thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rightCStr)),
                  rightCStr);

    double num = 1;
    auto numberNum = JSTaggedNumber(num);
    rightCStr = "1";
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, numberNum), rightCStr);

    num = 1.23;
    numberNum = JSTaggedNumber(num);
    rightCStr = "1.23";
    CheckOkString(thread_, JSHandle<JSTaggedValue>(thread_, numberNum), rightCStr);

    int numInt = 2;
    JSHandle<JSTaggedValue> value1(thread_, JSTaggedValue(numInt));
    rightCStr = "2";
    CheckOkString(thread_, JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToObject(thread_, value1)), rightCStr);

    num = 1.23;
    JSHandle<JSTaggedValue> value2(thread_, JSTaggedValue(num));
    rightCStr = "1.23";
    CheckOkString(thread_, JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToObject(thread_, value2)), rightCStr);

    bool valueBool = true;
    JSHandle<JSTaggedValue> value3(thread_, JSTaggedValue(valueBool));
    rightCStr = "true";
    CheckOkString(thread_, JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToObject(thread_, value3)), rightCStr);
}

TEST_F(JSTaggedValueTest, CanonicalNumericIndexString)
{
    JSTaggedValue result;

    JSHandle<EcmaString> str = thread_->GetEcmaVM()->GetFactory()->NewFromString("-0");
    JSTaggedValue tmpStr = str.GetTaggedValue();
    result = JSTaggedValue::CanonicalNumericIndexString(thread_, JSHandle<JSTaggedValue>(thread_, tmpStr));
    EXPECT_EQ(result.GetDouble(), -0.0);

    JSTaggedValue tmpInt(1);
    result = JSTaggedValue::CanonicalNumericIndexString(thread_, JSHandle<JSTaggedValue>(thread_, tmpInt));
    EXPECT_TRUE(result.IsUndefined());

    JSTaggedValue tmpDouble((double)100.0);
    result = JSTaggedValue::CanonicalNumericIndexString(thread_, JSHandle<JSTaggedValue>(thread_, tmpDouble));
    EXPECT_TRUE(result.IsUndefined());
}

TEST_F(JSTaggedValueTest, ToObject)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();

    // int -> JSObject
    JSHandle<JSTaggedValue> value1(thread_, JSTaggedValue(2));
    JSTaggedValue tagged1 =
        JSTaggedValue(JSHandle<JSPrimitiveRef>::Cast(JSTaggedValue::ToObject(thread_, value1))->GetValue());
    EXPECT_EQ(tagged1.GetRawData(), JSTaggedValue(2).GetRawData());

    // double -> JSObject
    JSHandle<JSTaggedValue> value2(thread_, JSTaggedValue(2.2));
    JSTaggedValue tagged2 =
        JSTaggedValue(JSHandle<JSPrimitiveRef>::Cast(JSTaggedValue::ToObject(thread_, value2))->GetValue());
    EXPECT_EQ(tagged2.GetRawData(), JSTaggedValue(static_cast<double>(2.2)).GetRawData());

    // bool -> JSObject
    JSHandle<JSTaggedValue> value3(thread_, JSTaggedValue::True());
    JSTaggedValue tagged3 =
        JSTaggedValue(JSHandle<JSPrimitiveRef>::Cast(JSTaggedValue::ToObject(thread_, value3))->GetValue());
    EXPECT_EQ(tagged3.GetRawData(), JSTaggedValue::True().GetRawData());

    // String -> JSObject
    JSHandle<JSTaggedValue> value4(factory->NewFromString("aaa"));
    JSTaggedValue tagged4 =
        JSTaggedValue(JSHandle<JSPrimitiveRef>::Cast(JSTaggedValue::ToObject(thread_, value4))->GetValue());
    EXPECT_TRUE(tagged4.IsString());
    EXPECT_EQ(reinterpret_cast<EcmaString *>(tagged4.GetRawData())->Compare(value4.GetObject<EcmaString>()), 0);

    // JSSymbol -> JSObject
    JSHandle<JSSymbol> symbol = factory->NewPublicSymbolWithChar("bbb");
    JSHandle<EcmaString> str = factory->NewFromString("bbb");
    JSHandle<JSTaggedValue> value5(symbol);
    JSTaggedValue tagged5 =
        JSTaggedValue(JSHandle<JSPrimitiveRef>::Cast(JSTaggedValue::ToObject(thread_, value5))->GetValue());
    EXPECT_EQ(EcmaString::Cast(reinterpret_cast<JSSymbol *>(tagged5.GetRawData())->GetDescription().GetHeapObject())
                  ->Compare(*str),
              0);
    EXPECT_TRUE(tagged5.IsSymbol());

    // JSObject(include all types of objects inherited from JSObject) -> JSObject
    EcmaVM *ecma = thread_->GetEcmaVM();
    JSHandle<JSTaggedValue> objectFun = ecma->GetGlobalEnv()->GetObjectFunction();
    JSHandle<JSObject> jsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFun), objectFun);
    JSHandle<JSTaggedValue> value(jsObj);
    EXPECT_EQ(*JSTaggedValue::ToObject(thread_, value), *jsObj);
}

TEST_F(JSTaggedValueTest, ToLength)
{
    JSTaggedNumber result;

    JSTaggedValue intV(100);
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, intV));
    EXPECT_EQ(result.GetNumber(), 100);

    JSTaggedValue intV2(-1);
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, intV2));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue doubleV1((double)100.0);
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, doubleV1));
    EXPECT_EQ(result.GetNumber(), (double)100.0);

    JSTaggedValue doubleV2((double)100.123);
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, doubleV2));
    EXPECT_EQ(result.GetNumber(), (double)100);

    JSTaggedValue doubleV3((double)-1.0);
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, doubleV3));
    EXPECT_EQ(result.GetNumber(), (double)0);

    JSTaggedValue doubleV4((double)9007199254740992);
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, doubleV4));
    EXPECT_EQ(result.GetNumber(), (double)9007199254740991);

    JSTaggedValue undefinedV = JSTaggedValue::Undefined();
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, undefinedV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue holeV = JSTaggedValue::Hole();
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, holeV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue nullV = JSTaggedValue::Null();
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, nullV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue falseV = JSTaggedValue::False();
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, falseV));
    EXPECT_EQ(result.GetNumber(), 0);

    JSTaggedValue trueV = JSTaggedValue::True();
    result = JSTaggedValue::ToLength(thread_, JSHandle<JSTaggedValue>(thread_, trueV));
    EXPECT_EQ(result.GetNumber(), 1);
}

TEST_F(JSTaggedValueTest, IsArray)
{
    EcmaVM *ecma = thread_->GetEcmaVM();
    JSHandle<JSTaggedValue> objectFun = ecma->GetGlobalEnv()->GetArrayFunction();

    JSHandle<JSObject> jsObj =
        thread_->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFun), objectFun);

    ASSERT_TRUE(jsObj->IsJSArray());
    ASSERT_FALSE(JSTaggedValue(1).IsArray(thread_));

    ASSERT_FALSE(thread_->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().IsArray(thread_));
}

TEST_F(JSTaggedValueTest, IsCallable_IsConstructor_IsExtensible)
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> jsFunction = thread_->GetEcmaVM()->GetFactory()->NewJSFunction(env);
    JSHClass *jsHclass = jsFunction->GetJSHClass();
    ASSERT_TRUE(jsFunction->IsCallable());
    jsHclass->SetConstructor(true);
    ASSERT_TRUE(jsFunction->IsConstructor());
    jsHclass->SetConstructor(false);
    ASSERT_FALSE(jsFunction->IsConstructor());
    jsHclass->SetExtensible(true);
    ASSERT_TRUE(jsFunction->IsExtensible());
    jsHclass->SetExtensible(false);
    ASSERT_FALSE(jsFunction->IsExtensible());
    ASSERT_FALSE(JSTaggedValue(1).IsExtensible(thread_));
    ASSERT_FALSE(JSTaggedValue(1).IsConstructor());
    ASSERT_FALSE(JSTaggedValue(1).IsCallable());
}

TEST_F(JSTaggedValueTest, IsInteger)
{
    ASSERT_TRUE(JSTaggedValue(1).IsInteger());
    ASSERT_TRUE(JSTaggedValue(1.0).IsInteger());
    ASSERT_TRUE(JSTaggedValue(-1.0).IsInteger());
    ASSERT_FALSE(JSTaggedValue(-1.1).IsInteger());
    ASSERT_FALSE(JSTaggedValue(1.1).IsInteger());
    ASSERT_FALSE(JSTaggedValue(std::numeric_limits<double>::infinity()).IsInteger());
    ASSERT_FALSE(JSTaggedValue((-1) * std::numeric_limits<double>::infinity()).IsInteger());
    ASSERT_TRUE(JSTaggedValue(0).IsInteger());
    ASSERT_TRUE(JSTaggedValue(0.0).IsInteger());
    ASSERT_FALSE(JSTaggedValue::True().IsInteger());
    ASSERT_FALSE(JSTaggedValue::Undefined().IsInteger());
    ASSERT_FALSE(JSTaggedValue::Null().IsInteger());
    ASSERT_FALSE(JSTaggedValue::False().IsInteger());
    ASSERT_FALSE(JSTaggedValue::Hole().IsInteger());
    ASSERT_FALSE(thread_->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().IsInteger());
}

TEST_F(JSTaggedValueTest, IsPropertyKey)
{
    ASSERT_TRUE(JSTaggedValue::IsPropertyKey(
        JSHandle<JSTaggedValue>(thread_->GetEcmaVM()->GetFactory()->NewFromString("test"))));
}

TEST_F(JSTaggedValueTest, IsRegExp)
{
    JSHandle<EcmaString> string = thread_->GetEcmaVM()->GetFactory()->NewFromString("test");
    JSHandle<JSTaggedValue> obj = JSHandle<JSTaggedValue>::Cast(string);
    ASSERT_FALSE(JSObject::IsRegExp(thread_, obj));
}

TEST_F(JSTaggedValueTest, SameValue)
{
    EcmaVM *ecma = thread_->GetEcmaVM();
    JSHandle<JSTaggedValue> objectFun = ecma->GetGlobalEnv()->GetObjectFunction();

    JSHandle<JSObject> jsObj = ecma->GetFactory()->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFun), objectFun);

    // not same type
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue::False()));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue::True()));
    ASSERT_FALSE(
        JSTaggedValue::SameValue(JSTaggedValue(1), ecma->GetFactory()->NewFromString("test").GetTaggedValue()));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(*jsObj)));
    JSHandle<JSTaggedValue> test(ecma->GetFactory()->NewFromString("test"));
    ASSERT_FALSE(JSTaggedValue::SameValue(test.GetTaggedValue(), JSTaggedValue(*jsObj)));

    // number compare
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(1)));
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(1.0)));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue(2.0)));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits<int>::quiet_NaN()), JSTaggedValue(2.0)));
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits<int>::quiet_NaN()),
                                         JSTaggedValue(std::numeric_limits<int>::quiet_NaN())));
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits<double>::quiet_NaN()),
                                         JSTaggedValue(std::numeric_limits<double>::quiet_NaN())));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(0.0), JSTaggedValue(-0.0)));
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(0), JSTaggedValue(-0)));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue(-1.0)));

    // string compare
    JSHandle<JSTaggedValue> test1(ecma->GetFactory()->NewFromString("test1"));
    ASSERT_FALSE(JSTaggedValue::SameValue(test.GetTaggedValue(), test1.GetTaggedValue()));
    ASSERT_TRUE(JSTaggedValue::SameValue(test.GetTaggedValue(), test.GetTaggedValue()));

    // bool compare
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::True(), JSTaggedValue::True()));
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::False(), JSTaggedValue::False()));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::False(), JSTaggedValue::True()));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::True(), JSTaggedValue::False()));

    // js object compare
    ASSERT_TRUE(JSTaggedValue::SameValue(jsObj.GetTaggedValue(), jsObj.GetTaggedValue()));

    // undefined or null compare
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::Undefined(), JSTaggedValue::Undefined()));
    ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::Null(), JSTaggedValue::Null()));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::Undefined(), JSTaggedValue::Null()));
    ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::Null(), JSTaggedValue::Undefined()));
}

TEST_F(JSTaggedValueTest, SameValueZero)
{
    // SameValueZero differs from SameValue only in its treatment of +0 and -0.
    ASSERT_TRUE(JSTaggedValue::SameValueZero(JSTaggedValue(0.0), JSTaggedValue(-0.0)));
}

TEST_F(JSTaggedValueTest, Less)
{
    JSHandle<JSTaggedValue> test(thread_->GetEcmaVM()->GetFactory()->NewFromString("test"));
    JSHandle<JSTaggedValue> test1(thread_->GetEcmaVM()->GetFactory()->NewFromString("test1"));
    JSHandle<JSTaggedValue> test2(thread_->GetEcmaVM()->GetFactory()->NewFromString("test2"));

    ASSERT_TRUE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1.0)),
                                    JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2.0))));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-0.0))));
    ASSERT_TRUE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)),
                                    JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2))));

    ASSERT_TRUE(JSTaggedValue::Less(thread_, test, test1));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, test2, test1));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), test1));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, test2, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2))));

    ASSERT_TRUE(JSTaggedValue::Less(thread_,
                                    JSHandle<JSTaggedValue>(thread_->GetEcmaVM()->GetFactory()->NewFromString("1")),
                                    JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2))));
    ASSERT_TRUE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)),
                                    JSHandle<JSTaggedValue>(thread_->GetEcmaVM()->GetFactory()->NewFromString("2"))));

    ASSERT_TRUE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                    JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True())));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False())));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null())));
    ASSERT_FALSE(JSTaggedValue::Less(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined())));
}

TEST_F(JSTaggedValueTest, Equal)
{
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1))));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False())));

    // number compare
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1))));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1.0)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1.0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-0.0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0)),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0)),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0)),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null())));

    JSHandle<JSTaggedValue> test(thread_->GetEcmaVM()->GetFactory()->NewFromString("test"));
    JSHandle<JSTaggedValue> test1(thread_->GetEcmaVM()->GetFactory()->NewFromString("test1"));
    JSHandle<JSTaggedValue> empty(thread_->GetEcmaVM()->GetFactory()->NewFromString(""));
    JSHandle<JSTaggedValue> char0(thread_->GetEcmaVM()->GetFactory()->NewFromString("0"));
    JSHandle<JSTaggedValue> char0Point0(thread_->GetEcmaVM()->GetFactory()->NewFromString("0.0"));
    JSHandle<JSTaggedValue> char1(thread_->GetEcmaVM()->GetFactory()->NewFromString("1"));
    JSHandle<JSTaggedValue> char1Point0(thread_->GetEcmaVM()->GetFactory()->NewFromString("1.0"));
    JSHandle<JSTaggedValue> charm1(thread_->GetEcmaVM()->GetFactory()->NewFromString("-1"));
    JSHandle<JSTaggedValue> charm0Point0(thread_->GetEcmaVM()->GetFactory()->NewFromString("-0.0"));
    JSHandle<JSTaggedValue> char0Point1(thread_->GetEcmaVM()->GetFactory()->NewFromString("-0.1"));

    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0)), char0));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)), char0));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1.0)), char1));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-1.0)), charm1));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)), charm0Point0));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)), char0Point1));

    // string compare
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, test, test));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, test, test1));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, test, empty));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, empty, empty));

    // ASSERT_FALSE(JSTaggedValue::Equal(JSTaggedValue(thread->GetEcmaVM()->GetFactory()->NewFromString(""))),
    //                                   JSTaggedValue(1)));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, char1, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1))));
    // ASSERT_FALSE(JSTaggedValue::Equal(
    //    JSTaggedValue(thread->GetEcmaVM()->GetFactory()->NewFromString("aaa"))),
    //    JSTaggedValue(0)));
    // ASSERT_FALSE(JSTaggedValue::Equal(
    //    JSTaggedValue(thread->GetEcmaVM()->GetFactory()->NewFromString("true"))),
    //    JSTaggedValue::True()));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, char1, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, char0, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, char0, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, char0, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null())));
    // boolean compare
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False())));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True())));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1.0))));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0))));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                     JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0))));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1))));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()),
                                      JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1.0))));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()), char0));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()), char0Point0));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()), char1));
    ASSERT_TRUE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()), char1Point0));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True()), char0));
    ASSERT_FALSE(JSTaggedValue::Equal(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False()), char1));
}

TEST_F(JSTaggedValueTest, StrictEqual)
{
    // This algorithm differs from the SameValue Algorithm in its treatment of signed zeroes and NaNs.
    ASSERT_TRUE(JSTaggedValue::StrictEqual(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0.0)),
                                           JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-0.0))));
    ASSERT_FALSE(JSTaggedValue::StrictEqual(
        thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(std::numeric_limits<double>::quiet_NaN())),
        JSHandle<JSTaggedValue>(thread_, JSTaggedValue(std::numeric_limits<double>::quiet_NaN()))));
}
}  // namespace ark::test

// NOLINTEND(readability-magic-numbers)
